﻿using System.Collections;
using System.Collections.Generic;
using Nomo.CoreModule;
using Nomo.UnityCoreModule;
using UnityEngine;
using UnityEngine.Networking;

namespace Nomo.Unity.WebRequestModule
{
    /// <summary>
    /// Unity 网络请求助理。
    /// </summary>
    public static class UnityWebHandler
    {
        /// <summary>
        /// 所有已经下载好的资源包信息。
        /// </summary>
        private static readonly Dictionary<string, AssetBundleInfo> AssetBundleInfos = new Dictionary<string, AssetBundleInfo>();

        /// <summary>
        /// 所有已经下载好的资源包信息的数量。
        /// </summary>
        public static int Count => AssetBundleInfos.Count;

        #region managed

        /// <summary>
        /// 发送 HTTP GET 请求以获取资源包。成功获取到的资源包可以通过 <see cref="GetAssetBundleInfo"/> 来获取。
        /// </summary>
        /// <param name="key">键，通常为资源包的名称。</param>
        /// <param name="args">Unity 网络请求参数。</param>
        public static void HttpGetAssetBundleManaged(string key, UnityWebRequestArgs<UnityWebRequestResultAssetBundle> args)
        {
            args.Succeeded = (result =>
            {
                Log.Info($"下载资源包 {args.Uri} 成功。");
                AssetBundleInfos.Add(key, new AssetBundleInfo(key, args.Uri, result.AssetBundle));
            }) + args.Succeeded;
            args.Failed = (result => Log.Error($"下载资源包 {args.Uri} 失败。")) + args.Failed;
            HttpGetAssetBundle(args);
        }

        /// <summary>
        /// 通过键获取一个已经下载完成的资源包信息。
        /// </summary>
        /// <param name="key">键。</param>
        /// <returns>一个键与传入的键值相等的下载好的资源包信息。</returns>
        public static AssetBundleInfo GetAssetBundleInfo(string key)
        {
            AssetBundleInfos.TryGetValue(key, out var assetBundleInfo);
            return assetBundleInfo;
        }

        /// <summary>
        /// 卸载所有通过 <see cref="HttpGetAssetBundleManaged"/> 下载的资源包。
        /// </summary>
        /// <param name="unloadAllLoadedObjects">在释放资源包时，是否也释放包含在它们之内的正在被使用的资源？</param>
        public static void UnloadAllManagedAssetBundleInfos(bool unloadAllLoadedObjects)
        {
            foreach (var pair in AssetBundleInfos)
            {
                pair.Value.AssetBundle.Unload(unloadAllLoadedObjects);
            }
        }

        /// <summary>
        /// 清空存放所有通过 <see cref="HttpGetAssetBundleManaged"/> 下载的资源包生成的资源包信息的序列。
        /// </summary>
        public static void ClearAllManagedAssetBundleInfos()
        {
            AssetBundleInfos.Clear();
        }

        #endregion

        #region 资源包

        /// <summary>
        /// 发送 HTTP GET 请求以获取资源包。
        /// </summary>
        /// <param name="args">Unity 网络请求参数。</param>
        public static void HttpGetAssetBundle(UnityWebRequestArgs<UnityWebRequestResultAssetBundle> args)
        {
            CoroutineRunner.Start(HttpGetAssetBundleEnumerator(args), out _);
        }

        private static IEnumerator HttpGetAssetBundleEnumerator(UnityWebRequestArgs<UnityWebRequestResultAssetBundle> args)
        {
            using (var request = UnityWebRequestAssetBundle.GetAssetBundle(args.Uri))
            {
                request.timeout = args.Timeout;

                yield return request.SendWebRequest();

                var result = new UnityWebRequestResultAssetBundle
                {
                    Error        = request.error,
                    ResponseCode = request.responseCode
                };
                if (!request.isHttpError && !request.isNetworkError)
                {
                    result.AssetBundle = DownloadHandlerAssetBundle.GetContent(request);
                    args.Succeeded?.Invoke(result);
                }
                else
                {
                    args.Failed?.Invoke(result);
                }
            }
        }

        #endregion

        #region GET

        /// <summary>
        /// 发送 HTTP GET 请求。
        /// </summary>
        /// <param name="args">Unity 网络请求参数。</param>
        public static void HttpGet(UnityWebRequestArgs<UnityWebRequestResult> args)
        {
            CoroutineRunner.Start(HttpGetEnumerator(args), out _);
        }

        private static IEnumerator HttpGetEnumerator(UnityWebRequestArgs<UnityWebRequestResult> args)
        {
            using (var request = UnityWebRequest.Get(args.Uri))
            {
                request.timeout = args.Timeout;

                yield return request.SendWebRequest();

                var result = new UnityWebRequestResult
                {
                    Data         = request.downloadHandler.data,
                    Text         = request.downloadHandler.text,
                    Error        = request.error,
                    ResponseCode = request.responseCode
                };
                if (!request.isHttpError && !request.isNetworkError)
                {
                    args.Succeeded?.Invoke(result);
                }
                else
                {
                    args.Failed?.Invoke(result);
                }
            }
        }

        #endregion

        #region POST

        /// <summary>
        /// 发送 HTTP POST 请求。
        /// </summary>
        /// <param name="args">Unity 网络请求参数。</param>
        public static void HttpPost(UnityWebRequestArgs<UnityWebRequestResult> args)
        {
            CoroutineRunner.Start(HttpPostEnumerator(args), out _);
        }

        private static IEnumerator HttpPostEnumerator(UnityWebRequestArgs<UnityWebRequestResult> args)
        {
            var form = new WWWForm();
            if (args.PostFormArgs != null)
            {
                foreach (var (fieldName, value) in args.PostFormArgs)
                {
                    form.AddField(fieldName.ToString(), value.ToString());
                }
            }
            using (var request = UnityWebRequest.Post(args.Uri, form))
            {
                request.timeout = args.Timeout;

                yield return request.SendWebRequest();

                var result = new UnityWebRequestResult
                {
                    Data         = request.downloadHandler.data,
                    Text         = request.downloadHandler.text,
                    Error        = request.error,
                    ResponseCode = request.responseCode
                };
                if (!request.isHttpError && !request.isNetworkError)
                {
                    args.Succeeded?.Invoke(result);
                }
                else
                {
                    args.Failed?.Invoke(result);
                }
            }
        }

        #endregion
    }
}
